package main

import (
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
	"math/rand"
	"strconv"
	"time"
)

type Lottery struct { }

type Student struct {
	Number   string `json:"number"`		// 报名号
	Username string `json:"username"`	// 姓名
	Sex      string `json:"sex"`		// 性别
	Brand    string `json:"brand"`		// 号牌
	State	 string `json:"state"`		// 状态码（0: 未摇中 1: 已摇中）
}

type School struct {

}

func (l *Lottery) Init(stub shim.ChaincodeStubInterface) peer.Response {
	for i := 1; i <= 50; i++ {
		Number := "A00" + strconv.Itoa(i)
		Username := "student" + strconv.Itoa(i)
		Sex := "男"
		res := l.addStudent(stub, []string{Number, Username, Sex})
		if res.Status != shim.OK {
			return shim.Error(res.Message)
		}
	}

	return shim.Success(nil)
}

func (l *Lottery) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	fcn, args := stub.GetFunctionAndParameters()
	fmt.Println("Invoke is Running " + fcn)
	switch fcn {
	case "addStudent":
		return l.addStudent(stub, args)
	case "queryStudent":
		return l.queryStudent(stub, args)
	case "randomLottery":
		return l.randomLottery(stub, args)
	default:
		return shim.Error("unknown function")
	}
}

func (l *Lottery) addStudent(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) < 3 {
		return shim.Error("args len Error")
	}
	number := args[0]
	username := args[1]
	sex := args[2]
	brand := strconv.FormatInt(time.Now().Unix(), 10) + strconv.Itoa(rand.Intn(100))

	if number == "" || username == "" || sex == "" {
		return shim.Error("invalid args")
	}
	student := &Student{
		Number:   number,
		Username: username,
		Sex:      sex,
		Brand:    brand,
		State:	  "0",
	}
	// 创建主键
	key, err := stub.CreateCompositeKey("student", []string{student.Brand})
	if err != nil {
		return shim.Error("CreateCompositeKey Error")
	}
	// 序列化
	byte, err := json.Marshal(student)
	if err != nil {
		return shim.Error("Marshal Error")
	}
	// 写入
	if err := stub.PutState(key, byte);err != nil {
		return shim.Error("PutState Error")
	}
 	return shim.Success(byte)
}

func (l *Lottery) queryStudent(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	result,err := stub.GetStateByPartialCompositeKey("student", []string{})
	if err != nil {
		return shim.Error("GetStateByPartialCompositeKey Error")
	}
	defer result.Close()
	var students []Student
	for result.HasNext() {
		val,err := result.Next()
		if err != nil {
			return shim.Error("Next Error")
		}
		var student Student
		if err := json.Unmarshal(val.GetValue(), &student); err != nil {
			return shim.Error("Unmarshal Error")
		}
		students = append(students, student)
	}
	byte,err := json.Marshal(students)
	if err != nil {
		return shim.Error("Marshal Error")
	}
	return shim.Success(byte)
}

func (l *Lottery) randomLottery(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	result,err := stub.GetStateByPartialCompositeKey("student", []string{})
	if err != nil {
		return shim.Error("GetStateByPartialCompositeKey Error")
	}
	defer result.Close()
	var students []Student
	for result.HasNext() {
		val,err := result.Next()
		if err != nil {
			return shim.Error("Next Error")
		}
		var student Student
		if err := json.Unmarshal(val.GetValue(), &student); err != nil {
			return shim.Error("Unmarshal Error")
		}
		students = append(students, student)
	}
	// 随机摇出百分之30的学生
	var lotteryResult []Student
	for i := 0; i < len(students)/3; i++ {
		num := rand.Intn(len(students))
		student := students[num]
		studentKey, err := stub.CreateCompositeKey("student", []string{student.Brand})
		if err != nil {
			return shim.Error("CreateKey Error")
		}
		student.State = "1"
		studentByte, err := json.Marshal(student)
		if err := stub.PutState(studentKey, studentByte); err != nil {
			return shim.Error("PutState Error")
		}
		lotteryResult = append(lotteryResult, student)
	}
	lotteryByte,err := json.Marshal(lotteryResult)
	if err != nil {
		return shim.Error("Marshal Error")
	}
	return shim.Success(lotteryByte)
}


func main()  {
	err := shim.Start(new(Lottery))
	if err != nil {
		fmt.Println("Start Error")
	}
}
